package org.team4.dao;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Restrictions;
import org.team4.exception.CRUDException;
import org.team4.util.ListHelper;
import org.team4.util.PageHelper;
import org.team4.util.Validate;
import org.team4.vo.Page;

/**
 * 泛型DAO<br/>
 * 
 * @author MarkerKing
 * 
 * @param <T>
 */
public class GenericDAO<T> implements Serializable {

	private static final long serialVersionUID = -8220641151762756281L;
	private final transient Log log = LogFactory.getLog(GenericDAO.class);
	private SessionFactory sessionFactory;
	private Class entityClass;

	private static ConcurrentHashMap<Class, GenericDAO> daos = new ConcurrentHashMap<Class, GenericDAO>();

	/**
	 * 获得实例
	 * 
	 * @param sessionFactory
	 * @param entityClass
	 * @return
	 */
	public static GenericDAO getInstance(SessionFactory sessionFactory, Class entityClass) {
		// 从池中获得实例
		GenericDAO dao = daos.get(entityClass);
		// 判断是否为空
		if (dao == null) {
			// 生成新实例
			dao = new GenericDAO(sessionFactory, entityClass);
			// 放入池中，key为Class，确保每个Domain只生成一个GenericDAO
			daos.put(entityClass, dao);
		}
		return dao;
	}

	/**
	 * 构造函数
	 * 
	 * @param sessionFactory
	 * @param entityClass
	 */
	protected GenericDAO(SessionFactory sessionFactory, Class entityClass) {
		this.sessionFactory = sessionFactory;
		this.entityClass = entityClass;
	}

	/**
	 * 保存对象
	 * 
	 * @param entity
	 *            需要保存的对象
	 * @throws CRUDException
	 */
	public void save(T entity) throws CRUDException {
		try {
			getSession().save(entity);
		} catch (Exception e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new CRUDException(e);
		}
	}

	/**
	 * 修改对象
	 * 
	 * @param entity
	 *            需要修改的对象
	 * @throws CRUDException
	 */
	public void update(T entity) throws CRUDException {
		try {
			getSession().update(entity);
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new CRUDException(e);
		}
	}

	/**
	 * 保存或修改<br/> 若被操作对象在数据库中不存在则保存,反之则修改
	 * 
	 * @param entity
	 *            需要操作的对象
	 * @throws CRUDException
	 */
	public void saveOrUpdate(T entity) throws CRUDException {
		try {
			getSession().saveOrUpdate(entity);
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new CRUDException(e);
		}
	}

	/**
	 * 删除对象
	 * 
	 * @param entity
	 *            需要删除的对象
	 * @throws CRUDException
	 */
	public void delete(T entity) throws CRUDException {
		try {
			getSession().delete(entity);
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new CRUDException(e);
		}
	}

	/**
	 * 按ID删除对象
	 * 
	 * @param id
	 *            需要删除的对象ID
	 * @throws CRUDException
	 */
	public void delete(Serializable id) throws CRUDException {
		T entity = get(id);
		if (entity != null)
			delete(entity);
	}

	/**
	 * 批量删除
	 * 
	 * @param collection
	 *            对象集合
	 * @throws CRUDException
	 */
	public void deleteAll(Collection<T> collection) throws CRUDException {
		try {
			for (Iterator iter = collection.iterator(); iter.hasNext();) {
				getSession().delete(iter.next());
			}
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new CRUDException(e);
		}
	}

	/**
	 * 批量删除
	 * 
	 * @param idList
	 *            对象ID集合
	 * @throws CRUDException
	 */
	public void deleteByIdList(List<String> idList) throws CRUDException {
		try {
			for (Iterator<String> iter = idList.iterator(); iter.hasNext();) {
				delete((Serializable) iter.next());
			}
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new CRUDException(e);
		}
	}

	/**
	 * 找出指定ID集合中不能被删除的ID集合
	 * 
	 * @param idList
	 * @return
	 * @throws CRUDException
	 */
	public List<List<String>> findCannotDeleteList(List<String> idList, List<String> sqlList) throws CRUDException {
		// 声明Set集合
		Set<String> cannotDeleteData = new HashSet<String>();
		// 循环SQL常量类中删除用到的SQL集合
		for (String hql : sqlList) {
			// 以ID为参数循环查询数据
			for (String id : idList) {
				// 如果查找到数据,则将这个ID放入不能被删除的集合中
				if (Validate.collectionNotNull(find(hql, id)))
					cannotDeleteData.add(id);
			}
		}
		// 返回不能被删除的List集合
		return ListHelper.getDifferent(idList, new ArrayList<String>(cannotDeleteData));
	}

	/**
	 * 查询全部
	 * 
	 * @return
	 * @throws CRUDException
	 */
	public List<T> findAll() throws CRUDException {
		return findByCriteria();
	}

	/**
	 * 按ID获取对象(LOAD)<br/> 如果未查询到数据,会抛出ObjectNotFoundEcception
	 * 
	 * @param id
	 * @return
	 * @throws CRUDException
	 */
	@SuppressWarnings("unchecked")
	public T load(final Serializable id) throws CRUDException {
		try {
			return (T) getSession().load(entityClass, id);
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new CRUDException(e);
		}
	}

	/**
	 * 按ID获取对象(GET)<br/> 如果未查询到数据,则返回NULL
	 * 
	 * @param id
	 * @return
	 * @throws CRUDException
	 */
	@SuppressWarnings("unchecked")
	public T get(final Serializable id) throws CRUDException {
		try {
			return (T) getSession().get(entityClass, id);
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new CRUDException(e);
		}
	}
	
	/**
	 * 返回游离状态的对象，一般在复制对象的时候使用，返回后请手动清空id，如果有禁止重复的字段，请自行处理
	 * @param id
	 * @return
	 * @throws CRUDException
	 */
	public T evictGet(final Serializable id) throws CRUDException {
		try {
			T entity = (T) getSession().get(entityClass, id);
			this.getSession().evict(entity);
			return entity;
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new CRUDException(e);
		}
	}

	/**
	 * 按HQL查询对象列表.
	 * 
	 * @param hql
	 *            hql语句
	 * @param values
	 *            数量可变的参数
	 * @throws CRUDException
	 */
	@SuppressWarnings("unchecked")
	public List<T> find(String hql, Object... values) throws CRUDException {
		try {
			return createQuery(hql, null, values).list();
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new CRUDException(e);
		}
	}

	public List<T> findOrder(String hql, String order, Object... values) throws CRUDException {
		try {
			return createQuery(hql, order, values).list();
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new CRUDException(e);
		}
	}

	/**
	 * 按HQL查询唯一对象.
	 * 
	 * @param hql
	 * @param values
	 * @return
	 * @throws CRUDException
	 */
	@SuppressWarnings("unchecked")
	public Object findUnique(String hql, Object... values) throws CRUDException {
		try {
			return (Object) createQuery(hql, null, values).uniqueResult();
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new CRUDException(e);
		}
	}

	/**
	 * 按Criterion查询对象列表.
	 * 
	 * @param criterion
	 *            数量可变的Criterion
	 * @return
	 * @throws CRUDException
	 */
	@SuppressWarnings("unchecked")
	private List<T> findByCriteria(Criterion... criterion) throws CRUDException {
		try {
			return createCriteria(criterion).list();
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new CRUDException(e);
		}
	}

	/**
	 * 按属性查找对象列表<br/> 示例代码<br/> xxx.findByProperty("name", "Lucy");
	 * 
	 * @param propertyName
	 *            属性名
	 * @param value
	 *            属性值
	 * @return
	 * @throws CRUDException
	 */
	@SuppressWarnings("unchecked")
	public List<T> findByProperty(String propertyName, Object value) throws CRUDException {
		try {
			return createCriteria(Restrictions.eq(propertyName, value)).list();
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new CRUDException(e);
		}
	}

	/**
	 * 按属性查找唯一对象.若对象不唯一会抛出异常哦<br/> 示例代码<br/>
	 * xxx.findUniqueByProperty("name","Lucy");
	 * 
	 * @param propertyName
	 *            属性名
	 * @param value
	 *            属性值
	 * @return
	 * @throws CRUDException
	 */
	@SuppressWarnings("unchecked")
	public T findUniqueByProperty(String propertyName, Object value) throws CRUDException {
		try {
			return (T) createCriteria(Restrictions.eq(propertyName, value)).uniqueResult();
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new CRUDException(e);
		}
	}

	/**
	 * 根据查询函数与参数列表创建Query对象,后续可进行更多处理,辅助函数.
	 * 
	 * @param hql
	 * @param values
	 * @param order
	 * @return
	 */
	private Query createQuery(String hql, String order, Object... values) {
		if (Validate.notNull(order)) {
			hql += " order by " + order;
		}
		Query queryObject = getSession().createQuery(hql);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				queryObject.setParameter(i, values[i]);
			}
		}
		queryObject.setCacheable(true);
		return queryObject;
	}

	/**
	 * 根据Criterion条件创建Criteria,后续可进行更多处理,辅助函数.
	 * 
	 * @param criterions
	 * @return
	 */
	private Criteria createCriteria(Criterion... criterions) {
		Criteria criteria = getSession().createCriteria(entityClass);
		for (Criterion c : criterions) {
			criteria.add(c);
		}
		criteria.setCacheable(true);
		return criteria;
	}

	/**
	 * 根据原生SQL查询
	 * 
	 * @param sql
	 * @return 返回集合,每个对象为Object[]
	 * @throws CRUDException
	 */
	public List findBySQL(String sql) throws CRUDException {
		try {
			return getSQLQuery(sql).list();
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new CRUDException(e);
		}
	}

	/**
	 * 根据原生SQL查询
	 * 
	 * @param sql
	 * @return 返回集合,每个对象为entityClass
	 * @throws CRUDException
	 */
	@SuppressWarnings("unchecked")
	public List<T> findBySQLAsEntity(String sql) throws CRUDException {
		try {
			return getSQLQuery(sql).addEntity(entityClass).list();
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new CRUDException(e);
		}
	}
	
	/**
	 * deleteFlag修改
	 * @param id
	 * @param flag
	 * @throws CRUDException
	 */
	public void updateDeleteFlag(String id, String deleteFlag) throws CRUDException {
		StringBuffer sql = new StringBuffer("update ");
		sql.append(entityClass.getSimpleName());
		sql.append(" set deleteFlag = ? where id = ?");
		executeUpdate(sql.toString(), deleteFlag, id);
	}
	
	/**
	 * 批量deleteFlag修改
	 * @param idList
	 * @param flag
	 * @throws CRUDException
	 */
	public void updateDeleteFlag(List<String> idList, String deleteFlag) throws CRUDException {
		for (String id : idList) {
			updateDeleteFlag(id, deleteFlag);
		}
	}

	/**
	 * 以SQL方式执行语句
	 * 
	 * @param sql
	 * @param values
	 * @throws CRUDException
	 */
	public void executeUpdate(String sql, Object... values) throws CRUDException {
		try {
			SQLQuery query = getSQLQuery(sql);
			if (Validate.arrayNotNull(values)) {
				for (int i = 0; i < values.length; i++) {
					query.setParameter(i, values[i]);
				}
			}
			query.executeUpdate();
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new CRUDException(e);
		}
	}

	/**
	 * 分页方法
	 * 
	 * @param page
	 * @param hql
	 * @param values
	 * @return
	 * @throws CRUDException
	 */
	@SuppressWarnings("unchecked")
	public Page<T> pagination(Page<T> page, String hql, String orderBy, Object... values) throws CRUDException {
		if (Validate.notNull(orderBy)) {
			orderBy = " order by " + orderBy;
		} else {
			orderBy = "";
		}

		Query query = createQuery(hql + orderBy, null, values);

		// 设置分页参数
		query.setFirstResult(PageHelper.getFirstRow(page.getCurrPage(), page.getPageSize()));
		query.setMaxResults(page.getPageSize());

		// count效率高
		Query countQuery = createQuery("SELECT COUNT(id) " + hql, null, values);

		// 设置查询缓存
		query.setCacheable(true);
		countQuery.setCacheable(true);

		// 查找总行数
		int rowCount = ((Long) countQuery.uniqueResult()).intValue();

		// 结果放入分页类
		page.setRowCount(rowCount);
		page.setList(query.list());
		page.setMaxPage(PageHelper.getMaxPage(rowCount, page.getPageSize()));
		// 返回分页类
		return page;
	}

	/**
	 * 分页方法
	 * 
	 * @param page
	 * @param hql
	 * @param values
	 * @return
	 * @throws CRUDException
	 */
	@SuppressWarnings("unchecked")
	public Page<T> paginationBySQL(Page<T> page, String hql, String orderBy, Object... values) throws CRUDException {
		if (Validate.notNull(orderBy)) {
			orderBy = " order by " + orderBy;
		} else {
			orderBy = "";
		}

		SQLQuery query = getSession().createSQLQuery("select * " + hql + orderBy);
		if (Validate.arrayNotNull(values)) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}

		// 设置分页参数
		query.setFirstResult(PageHelper.getFirstRow(page.getCurrPage(), page.getPageSize()));
		query.setMaxResults(page.getPageSize());

		// count效率高
		SQLQuery countQuery = getSession().createSQLQuery("SELECT COUNT(id) " + hql);
		if (Validate.arrayNotNull(values)) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		// 查找总行数
		int rowCount = (Integer) countQuery.uniqueResult();
		// 结果放入分页类
		page.setRowCount(rowCount);
		page.setList(query.list());
		page.setMaxPage(PageHelper.getMaxPage(rowCount, page.getPageSize()));
		// 返回分页类
		return page;
	}

	/**
	 * 获得SQLQuery
	 * 
	 * @param sql
	 * @return
	 */
	private SQLQuery getSQLQuery(String sql) {
		SQLQuery query = getSession().createSQLQuery(sql);
		return query;
	}

	/**
	 * 获取原生Session
	 * 
	 * @return
	 */
	public Session getSession() {
		return sessionFactory.getCurrentSession();
	}
}